//
//  UIColor+Codable.swift
//  MKCodableTests
//
//  Created by 苏尚进 on 2020/5/21.
//  Copyright © 2020 moon. All rights reserved.
//

import Foundation
import UIKit
import MKCodable

public struct UIColorCodingConvertor : CodingConvertor {
    public enum EncodingStrategy {
        case hex//十六进制符号 #RRGGBB 和 #RGB（比如 #ff0000）。"#" 后跟 6 位或者 3 位十六进制字符（0-9, A-F）
        //        #f03
        //        #F03
        //        #ff0033
        //        #FF0033

        case rgb//红-绿-蓝（red-green-blue (RGB)）
        //        规定颜色值为 rgb 代码的颜色，函数格式为 rgb(R,G,B)，取值可以是 0-255 的整数或百分比。
        //        rgb(255,0,51)
        //        rgb(255, 0, 51)
        //        rgb(100%,0%,20%)
        //        rgb(100%, 0%, 20%)

        case rgba//红-绿-蓝-阿尔法（RGBa）
        //        RGBa 扩展了 RGB 颜色模式，它包含了阿尔法通道，允许设定一个颜色的透明度。a 表示透明度：0=透明；1=不透明。
        //        rgba(255,0,0,0.1)    /* 10% 不透明 */
        //        rgba(255,0,0,0.4)    /* 40% 不透明 */
        //        rgba(255,0,0,0.7)    /* 70% 不透明 */
        //        rgba(255,0,0,  1)    /* 不透明，即红色 */

        case hsl//色相-饱和度-明度（Hue-saturation-lightness）
        //        色相（Hue）表示色环（即代表彩虹的一个圆环）的一个角度。
        //        饱和度和明度由百分数来表示。
        //        100% 是满饱和度，而 0% 是一种灰度。
        //        100% 明度是白色， 0% 明度是黑色，而 50% 明度是"一般的"。

        //        hsl(120,100%,25%)    /* 深绿色 */
        //        hsl(120,100%,50%)    /* 绿色 */
        //        hsl(120,100%,75%)    /* 浅绿色 */

        case hsla//色相-饱和度-明度-阿尔法（HSLa）
        //        HSLa 扩展自 HSL 颜色模式，包含了阿尔法通道，可以规定一个颜色的透明度。 a 表示透明度：0=透明；1=不透明。
        //        hsla(240,100%,50%,0.05)   /* 5% 不透明 */
        //        hsla(240,100%,50%, 0.4)   /* 40% 不透明 */
        //        hsla(240,100%,50%, 0.7)   /* 70% 不透明 */
        //        hsla(240,100%,50%,   1)   /* 完全不透明 */
    }
    var encodingStrategy:EncodingStrategy
    public init(_ encodingStrategy:EncodingStrategy = .hex) {
        self.encodingStrategy = encodingStrategy
    }
    public func decode(from coder: Coder) throws -> UIColor {
        if let s:String = try coder.decode() {
            let scanner = Scanner.init(string: s)
            if let color = scanner.mk.scanColor() {
                return color
            } else {
                throw CodingError.invalidValue(s, CodingError.Context(codingPath: coder.codingPath, debugDescription: "Expect color name or rgb(r,g,b) or rgba(r,g,b,a) or #RRGGBB or #RGB or hsl(h,s,l) or hsla(h,s,l,a)"))
            }
        }
        throw CodingError.dataCorrupted(CodingError.Context(codingPath: coder.codingPath, debugDescription: "Expect color name or rgb(r,g,b) or rgba(r,g,b,a) or #RRGGBB or #RGB or hsl(h,s,l) or hsla(h,s,l,a)"))
    }
    
    public func encode(to coder: Coder, value: UIColor) throws {
        var string:String = ""
        switch encodingStrategy {
        case .hex://#RRGGBB
            let (r,g,b,_) = value.mk.rgba
            string = "#\(String(format: "%.2x", Int(r*255)))\(String(format: "%.2x", Int(g*255)))\(String(format: "%.2x", Int(b*255)))"
            break
        case .rgb:
            let (r,g,b,_) = value.mk.rgba
            string = "rgb(\(String(Int(r*255))),\(String(Int(g*255))),\(String(Int(b*255))))"
            break;
        case .rgba://rgba(255,0,0,0.1)
            let (r,g,b,a) = value.mk.rgba
            string = "rgba(\(String(Int(r*255))),\(String(Int(g*255))),\(String(Int(b*255))),\(String(a)))"
            break;
        case .hsla://hsla(240,100%,50%,0.05)
            let (h,s,l,a) = value.mk.hsba
            string = "hsla(\(String(Int(h*255))),\(String(Int(s*100)))%,\(String(Int(l*100)))%,\(String(a)))"
            break;
        case .hsl:
            let (h,s,l,_) = value.mk.hsba
            string = "hsl(\(String(Int(h*255))),\(String(Int(s*100)))%,\(String(Int(l*100)))%)"
            break
        }
        try coder.encode(string)
    }
    
    public typealias CodingType = UIColor
    
}

extension UIColor : Codable {
    public static func initObject(from coder: Coder) throws -> Self {
        let obj:UIColor = try coder.decode(convertor: UIColorCodingConvertor())
        return obj as! Self
    }
    public func code(coder: Coder) throws {
        if coder.isDecoding {
            throw CodingError.canNotDecodeTo(CodingError.Context(codingPath: coder.codingPath, debugDescription: "Can not decode coder value to UIColor Object"))
        } else {
            try UIColorCodingConvertor().encode(to: coder, value: self)
        }
    }
}
